#pragma once
#include <Common/error.h>
#include <Compiler/compiler.h>
#include <Compiler/error_reporter.h>
#include <VM/chunk.h>
#include <memory>
#include <unordered_map>
#include <vector>

enum class InterpretResult
{
	OK,
	COMPILE_ERROR,
	RUNTIME_ERROR
};

struct CallFrame
{
	FunctionObject *function;
	// 没有使用迭代器，因为迭代器可能失效
	size_t ip;			 // 指向chunk->code的偏移量，初始化为当前调用窗口的初始位置
	size_t stack_offset; // 指向VM::valueStack的偏移量，表示窗口中变量的绝对起始位置

	CallFrame(FunctionObject *func, size_t ip, size_t offset) : function(func), ip(ip), stack_offset(offset) {}
};

// Virtual Machine
class VM
{
public:
	VM();
	InterpretResult interpret(std::string_view source);

private:
	void execute();
	void resetVM();
	void runtimeError(std::string error_message);

	// 检查栈顶元素是否为需求元素
	template <typename T>
	bool peekIs() const;
	// 检查次顶元素是否为需求元素
	template <typename T>
	bool peekSecondIs() const;
	Value pop();

	template <typename T>
	T expect(size_t offset, std::string message, bool shouldPop);
	double peekNumberOperand(size_t offset) { return expect<double>(offset, "Operand must be a number", false); }
	double popNumberOperand(size_t offset) { return expect<double>(offset, "Operand must be a number.", true); }

	void callValue(Value callee, int argNums);
	void call(FunctionObject *function, int argCount);

private:
	std::vector<CallFrame> frames;
	std::vector<Value> valueStack;
	std::unordered_map<std::string, Value> globals;

	Chunk *chunk; // 指向当前调用窗口的chunk

	ErrorReporter reporter{};
	Compiler compiler{reporter};
};

template <typename T>
bool VM::peekIs() const
{
	return std::holds_alternative<T>(valueStack.back());
}

template <typename T>
bool VM::peekSecondIs() const
{
	// FIXME 可能导致越界访问
	return std::holds_alternative<T>(valueStack.rbegin()[1]);
}

template <typename T>
T VM::expect(size_t offset, std::string errorMessage, bool shouldPop)
{
	if (!peekIs<T>())
		throw Error(chunk->getLine(offset), std::move(errorMessage));

	return std::get<T>(shouldPop ? pop() : valueStack.back());
}